/**
 * 
 */
function MeetingListBuilder() {
	// set global instance
	_builder = this; 
	
	this.defaultAddress = new Address(); 
	this.defaultLatLng = null; 

	this.sortType;
	
	this.searchLocation = new Location();
	
	this.model = [];
	this.locations = [];
	
	this.map = null;
	this.searchMarker = null;

	this.messageElem;
	this.mapElem;
	this.resultsElem;

	this.markerIcons = [];
	this.defaultMarkerIcon = "";
	
	this.queryURL = "";
	
	this.buildMeetingList = function() {
		
		var address = "";
		
		if (this.searchAddress) {
			address = this.searchAddress;
		} else if (this.searchLocation.address && 
				!this.searchLocation.address.isEmpty()) {
			address = this.searchLocation.address.formatAddress(); 
		} 
		
		if (address) {
			var geocoder = new google.maps.Geocoder();
			geocoder.geocode( 
				{ 'address': address }, 
				function(results, status) {
					_builder.onGeocodeResults(results, status);
				});
		} else {
			this.queryMeetings();
		}
	};

	this.queryMeetings = function() {
		var url = this.queryURL;
	
		if (this.searchLocation.latLng) {
			url += "&lat=" + this.searchLocation.latLng.lat() + 
				"&lng=" + this.searchLocation.latLng.lng();
		}
		
		$.ajax(url, 
			{
				type: "GET",
				dataType: "json",
				success: function(results) {
					_builder.onQueryResults(results);
				},
				error: function(xhr, status) {
					_builder.onQueryError(xhr.responseText);
				}
			});
			
		return;	
	};

	this.addMeetingLocation = function(meeting) {
		
		var latlng = (meeting.lat && meeting.lng) ? 
				new google.maps.LatLng(meeting.lat, meeting.lng) : null;
		var address = new Address(meeting.address1, meeting.city, meeting.zip)

		var location = this.findLocation(latlng, address);
		if (location) {
			location.updateFromMeeting(meeting);
			return location;
		}
		
		var markerIcon;
		if (latlng) {
			var idx = 0; 
			for (var i = 0; i < this.locations.length; ++i) {
				if (this.locations[i].markerIcon) {
					++idx;
					if (idx >= this.markerIcons.length) {
						break;
					}
				}
			}
			
			markerIcon = (idx >= this.markerIcons.length) ?
					this.defaultMarkerIcon : this.markerIcons[idx];
		} else {
			markerIcon = null;
		}
		
		location = new Location(
				this.locations.length,
				latlng, 
				markerIcon, 
				address,  
				meeting.location, 
				meeting.address2, 
				meeting.locationNotes);
		
		this.locations.push(location);
		
		return location;
	};

	this.findLocation = function(latlng, address) { 
		
		for (var i = 0; i < this.locations.length; ++i) {
			
			var location = this.locations[i];
			if (latlng && location.latLng) {
				
				if (latlng.lat() == location.latLng.lat() 
						&& latlng.lng() == location.latLng.lng()) {
					return location;
				}
				
			} else if (location.address.equals(address)) {
				return location;
			}
		}
		
		return null;
	}

	this.sortModelOnDayTime = function() {
		
		this.model.sort(function(meeting1,meeting2) {
			var i = _builder.compareDayTime(meeting1, meeting2)
			if (i == 0) { 
				return _builder.compareLocation(meeting1, meeting2);
			}
			return i;
		});
	};

	this.sortModelOnLocation = function() {
		
		this.model.sort(function(meeting1,meeting2) {
			var i = _builder.compareLocation(meeting1, meeting2)
			if (i == 0) { 
				return _builder.compareDayTime(meeting1, meeting2);
			}
			return i;
		});
	};
	
	this.compareDayTime = function(meeting1, meeting2) {
		var day1 = Number(meeting1.daySequence);
		var day2 = Number(meeting2.daySequence);
		
		if (day1 < day2) {
			return -1;
		} else if (day1 > day2) {
			return 1;
		}
		
		var time1 = Number(meeting1.timeSequence);
		var time2 = Number(meeting2.timeSequence);
		
		if (time1 < time2) {
			return -1;
		} else if (time1 > time2) {
			return 1;
		}
		
		return 0;
	};

	this.compareLocation = function(meeting1, meeting2) {
		var distance1 = Number(meeting1.distance);
		var distance2 = Number(meeting2.distance);
		
		if (distance1 < distance2) {
			return -1;
		} else if (distance1 > distance2) {
			return 1;
		}

		if (meeting1.city < meeting2.city) {
			return -1;
		} else if (meeting1.city > meeting2.city) {
			return 1
		}

		if (meeting1.address1 < meeting2.address1) {
			return -1;
		} else if (meeting1.address1 > meeting2.address1) {
			return 1;
		}
		
		return 0;
	};

	this.displayResults = function() {
		
		this.clearMarkers();
		this.locations = [];
		
		if (this.sortType == "location") {
			this.displayLocationTable();
		} else { 
			this.displayDayTimeTable();
		}
		this.displayMarkers();
	}

	this.displayMap = function (latlng) { 
		
		if (!this.map) {
			
			var mapOptions = {
		            center: latlng,
		            zoom: 15
		          };
		    
			var mapElem = $("#"+this.mapElem); 
			var anchor = $("<a name=\"_mapHeader\"></a>");
			mapElem.append(anchor);
			var mapDiv = $("<div></div>").prop("id", "_mapContainer")
				.width("100%").height("100%").css({"margin-right":"auto","margin-left":"auto"})
				.appendTo(mapElem).get(0);
		   	this.map = new google.maps.Map(mapDiv, mapOptions);
		
		} else {
			this.map.setCenter(latlng);
		}
	};
	this.displayDayTimeTable = function() { 
		var table = "<table class=\"results\" id=\"resultsTable\">";
		var lastDay;
		var rowCount = 1;
		
		for (i = 0; i < this.model.length; ++i) { 
			
			var meeting = this.model[i];

			if (lastDay != meeting.day) {
				table += "<tr class=\"results resultsHeader\">";
				
				table += "<td class=\"results imageCol\"></td>";
				
				var header = "";
				if (meeting.location) {
					header = meeting.location + "&nbsp;&nbsp;&nbsp;";
				} 
				
				table += "<td class=\"results headerCol\" colspan=\"5\">" + meeting.day + "</td>";
				table += "</tr>";
				lastDay = meeting.day;
				rowCount = 1;
			}

			table += "<tr class=\"results ";

			if (rowCount % 2) {
				table += "oddRow";
			} else {
				table += "evenRow";
			}
			
			table += "\">";
			
			var location = this.addMeetingLocation(meeting);
			var anchor = "<a href=\"#_mapHeader\" "
				+ " onclick=\"_builder.onLocationClick(" + location.lidx + ")\">";
			
			table += "<td class=\"results imageCol\">";
			if (location.markerIcon && location.markerIcon != this.defaultMarkerIcon) {
				table += "<img class=\"markerImage\" src=\""+location.markerIcon+"\">";
			}
	
			table += "</td><td class=\"results timeCol\">" + anchor + meeting.time + "</a></td>";
			table += "<td class=\"results nameCol\">" + anchor + meeting.name + "</a></td>";
	
	
			var locString = (meeting.location) ? meeting.location : meeting.address1;
			table += "<td class=\"results locationCol\">" + anchor + locString + "</a></td>";
		
			var notes = "";
			if (meeting.groupNotes) {
				notes += meeting.groupNotes;
			}
			if (meeting.locationNotes) {
				if (notes) { 
					notes += ", ";
				}
				notes += meeting.locationNotes;
			}
			
			table += "<td class=\"results notesCol\">" + anchor + notes + "</a></td>";
			table += "<td class=\"results codesCol\" title=\"" + codesToDescs(meeting.codes) 
					+ "\">"  + anchor +  meeting.codes + "</a></td>";
			table += "</tr>"
			++location.meetingCount;
			++this.searchLocation.meetingCount;
			++rowCount;
		}
		$("#" + this.resultsElem).html(table);
	};

	this.displayLocationTable = function() {
		
		var table = "<table class=\"results\" id=\"resultsTable\">";
		var currentLoc;
		
		var rowCount = 1;
		var headerCount = 0;
		for (i = 0; i < this.model.length; ++i) { 
			
			var meeting = this.model[i];
		
			var location = this.addMeetingLocation(meeting);
			
			if (location !== currentLoc) {
				++headerCount; 
				
				table += "<tr class=\"results resultsHeader\">";
			
				table += "<td class=\"results imageCol\">";
				// only show the icon in the table for the non default markers 
				if (location.markerIcon && location.markerIcon != this.defaultMarkerIcon) {
					table += "<img class=\"markerImage\" src=\""+location.markerIcon+"\">";
				}
				table += "</td><td class=\"results headerCol\" colspan=\"4\">";
				
				var header = "";
				if (meeting.location) {
					header = meeting.location + "&nbsp;&nbsp;&nbsp;";
				} 
				
				header += location.address.formatAddress();
				
				table += "<span class=\"results\">"+header+"</span>";
				
				location.headerName = "header_" + headerCount; 
				table += "<a name=\"" + location.headerName + "\" />";	
				table += "</td><td class=\"results distanceCol\">";
						
				if (meeting.distance || meeting.distance == 0) {
					table += meeting.distance + " Mi.";	
				}
				table += "</td>";
			
				table += "</tr>";
				
				currentLoc = location;
				rowCount = 1;
			}

			table += "<tr class=\"results ";
			if (rowCount % 2) {
				table += "oddRow";
			} else {
				table += "evenRow";
			}
			table += "\">";

			var queryString = "group=" + meeting.name + "&address=" + meeting.address1 + 
				"&city=" + meeting.city + "&zip=" + meeting.zip;
			queryString = queryString.replace(/ /g, "+");

			var anchor = "<a href=\"#_mapHeader\" "
				+ " onclick=\"_builder.onLocationClick(" + location.lidx + ")\">";
			
			table += "<td class=\"results imageCol\"></td>";
			table += "<td class=\"results dayCol\">" + anchor + meeting.day.substring(0,3) + "</a></td>";
			table += "<td class=\"results timeCol\" >" + anchor + meeting.time + "</a></td>";
			table += "<td class=\"results nameCol\" >" + anchor + meeting.name + "</a></td>";

			var notes = "";
			if (meeting.groupNotes) {
				notes += meeting.groupNotes;
			}
			if (meeting.locationNotes) {
				if (notes) { 
					notes += "<br>";
				}
				notes += meeting.locationNotes;
			}

			table += "<td class=\"results notesCol\" >" + anchor + notes + "</a></td>";
			table += "<td class=\"results codesCol\" title=\"" + codesToDescs(meeting.codes) 
						+ "\">" + anchor + meeting.codes + "</a></td>";
				
			table += "</tr>";
			++this.searchLocation.meetingCount;
			++currentLoc.meetingCount;
			++rowCount;
		}
		
		table += "</table>";
		$("#" + this.resultsElem).html(table);

	};

	this.displayMarkers = function() {

		var searchLatLng = this.searchLocation.latLng;
		
		for (var i = 0; i < this.locations.length; ++i) {

			var location = this.locations[i];
			if (!location.latLng) {
				/* bad address, don't add a marker */
				continue;
			}

			if (!this.map) {
				this.displayMap(location.latLng);
			}
	
			var markeropts = {};
			markeropts.position = location.latLng; 
			markeropts.title = (location.location) ? location.location : location.address.address;
			markeropts.icon = location.markerIcon;
			
			markeropts.map = this.map;
			location.marker = new google.maps.Marker(markeropts);

			google.maps.event.addListener(location.marker, 'click', 
					function (evt) { _builder.onMarkerClick(this, evt)});
			
			
			if (searchLatLng && searchLatLng.lat() == location.latLng.lat() 
					&& searchLatLng.lng() == location.latLng.lng()) {
				this.searchLocation.marker = location.marker;
			}
		}
		
		if (searchLatLng && !this.searchLocation.marker) {
			var markeropts = {};
			markeropts.position = searchLatLng; 
			markeropts.title = this.searchLocation.address.formatAddress();
			markeropts.icon = this.searchLocation.markerIcon;
			markeropts.map = this.map;
			this.searchLocation.marker = new google.maps.Marker(markeropts);
			google.maps.event.addListener(this.searchLocation.marker, 'click', 
					function (evt) { _builder.onMarkerClick(this, evt)});
	
		}
	};

	this.clearMarkers = function() { 
		
		for (var i = 0; i < this.locations.length; ++i) {
			if (this.locations[i].marker) {
				this.locations[i].marker.setMap(null);
				this.locations[i].marker = null;
			}
		}
	};

	this.displayInfoWindow = function(location) { 
		
		if (this.openInfoWindow) { 
			this.openInfoWindow.close();
			this.openInfoWindow = null;
		}
		
		if (!location.infoWindow) { 
			
			var content = "";
			
			if (location.location) {
				content += location.location;
			}
			
			if (location.address.address) {
				if (content) {
					content += "<br>";
				}
				content += location.address.address; 
			}
			if (location.address2) {
				if (content) {
					content += "<br>";
				}
				content += location.address2;
			}
			
			
			if (location.address.city) { 
				if (content) {
					content += "<br>";
				} 
					
				content += location.address.city + ", ";
			}
			
			content += "GA";
			
			if (location.address.zip) {
				content += "&nbsp;&nbsp;" + location.address.zip;
			}
			
			var address = location.address.formatAddress().replace(/ /g, "+");
			content += "<p><a href=\"https://maps.google.com/maps?daddr=" 
				+ address + "\" target=\"_blank\" >Directions</a>";
			
			if (location.headerName) {
				content += "&nbsp;&nbsp;&nbsp;<a href=\"#" + location.headerName + "\" >"
					+ location.meetingCount + " Meetings Found</a>";
			} else { 
				content += "&nbsp;&nbsp;&nbsp;" + location.meetingCount + " Meetings Found";
			}
			
			content = "<span>" + content + "</span>";
			location.infoWindow = new google.maps.InfoWindow({content: content});
		}
		
		location.infoWindow.open(this.map, location.marker);
		this.openInfoWindow = location.infoWindow;
	};
	
	this.onGeocodeResults = function(results, status) {

		if (status == google.maps.GeocoderStatus.OK) {
			var latlng = results[0].geometry.location;	
  			this.searchLocation.latLng = new google.maps.LatLng(
  					latlng.lat().toFixed(7), latlng.lng().toFixed(7));
  			
  			if (this.searchAddress) {
  				
  				for (var i = 0; i < results[0].address_components.length; ++i) {
  					var comp = results[0].address_components[i];
  					
  					for (var ci = 0; ci < comp.types.length; ++ci) {
  						if (comp.types[ci] == "street_address") {
  							this.searchLocation.address.address = comp.short_name;
  						} else if (comp.types[ci] == "locality") {
  							this.searchLocation.address.city = comp.short_name;
  						} else if (comp.types[ci] == "postal_code") {
  							this.searchLocation.address.zip = comp.short_name;
  						}
  					}	
  					
  				}
  			}
  			
  		} else {
  			
  			this.searchLocation.latLng = this.defaultLatLng;
  			this.searchLocation.address = this.defaultAddress;
			  
  		}
		
		this.queryMeetings();
		this.displayMap(this.searchLocation.latLng);
	};

	this.onQueryResults = function(results) {
		
		if (results.status == "OK") {
			this.model = results.results;
			this.sortType = results.sort;
			this.displayResults();
		
			if (this.messageElem) {
				$("#" + this.messageElem).append("<span class=\"results messageText\">"
						+this.model.length + " Meetings Found</span>");		
			}
			
		} else {
			this.onQueryError(results.message);
		}
		
		if (!this.map && this.defaultLatLng) {
			this.displayMap(this.defaultLatLng);
		}
	}; 

	this.onQueryError = function(message) { 
		if (this.messageElem) {
			var m = "Error with search";
			if (message) {
				m += ": " + message;
			}
			m += ", please try again later";
			
			$("#" + this.messageElem).append("<span class=\"results errorText\">"+ m + "</span>");
		}
	};
	
	this.onLocationClick = function(idx) {
		var location = this.locations[idx];
		if (this.map && location && location.latLng) {
			this.map.setCenter(location.latLng);
			this.displayInfoWindow(location);
		}
	};

	this.onMarkerClick = function(marker, evt) {
		
		var location = this.findLocation(evt.latLng);
		if (location) { 
			this.displayInfoWindow(location);
		} else if (this.searchLocation.marker === marker) {
			this.displayInfoWindow(this.searchLocation);
		}
	};
	
	this.onSortChanged = function(value) {
	
		if (value != this.sortType) {
			
			this.sortType = value;
			if (this.sortType == "location") {
				this.sortModelOnLocation();
			} else {
				this.sortModelOnDayTime();
			}
			
			this.displayResults();
		}
	};
};

function Address(address, city, zip) {
	
	this.address = (address) ? $.trim(address) : "";
	this.city = (city) ? $.trim(city) : "";
	this.zip = (zip) ? $.trim(zip) : "";

	this.formatAddress = function() {
		var fulladdr = "";
		
		if (this.address) {
			fulladdr += this.address;
		}
		
		if (this.city) {
			if (fulladdr) {
				fulladdr += ", ";
			}
			fulladdr += this.city;
		}

		if (fulladdr || this.zip) {
			if (fulladdr) {
				fulladdr += ", ";
			}
			fulladdr += "GA";
		}
		
		if (this.zip) {
			fulladdr += ", " + this.zip;
		}
		
		return fulladdr;
	};

	this.equals = function(addr) {
		
		if (!addr) {
			return false;
		}
		
		if (this.address) {
			if (!addr.address) {
				return false;
			}
			if (this.address.toUpperCase() != addr.address.toUpperCase()) {
				return false;
			}
		} else if (!this.address) {
			if (addr.address) { 
				return false;
			}
		}

		if (this.city) {
			if (!addr.city) {
				return false;
			}
			if (this.city.toUpperCase() != addr.city.toUpperCase()) {
				return false;
			}
		} else if (!this.city) {
			if (addr.city) { 
				return false;
			}
		}

		if (this.zip) {
			if (!addr.zip) {
				return false;
			}
			if (this.zip.toUpperCase() != addr.zip.toUpperCase()) {
				return false;
			}
		} else if (!this.zip) {
			if (addr.zip) { 
				return false;
			}
		}

		return true;
	};
	
	this.isEmpty = function() {
		return (!this.address && !this.city && !this.zip);
	};
};

function Location(idx, latlng, markerIcon, address, location, address2, locationNotes) {
	
	this.lidx = idx;
	this.latLng = latlng;
	this.markerIcon = (markerIcon) ? markerIcon : "";
	this.address = (address) ? address : new Address();
	this.location = (location) ? location : "";
	this.address2 = (address2) ? address2 : "";
	this.locationNotes = (locationNotes) ? locationNotes : "";
	this.marker = null;
	this.infoWindow = null;
	this.headerName = null;
	this.meetingCount = 0;
	
	this.updateFromMeeting = function(meeting) {
		
		if (!this.latLng) {
			if (meeting.lat && meeting.lng) {
				this.latLng = new google.maps.LatLng(meeting.lat.toFixed(7), meeting.lng.toFixed(7));
			}  
		}
		
		if (!this.address.address) {
			this.address.address = $.trim(meeting.address1);
		}
		if (!this.address.city) {
			this.address.city = $.trim(meeting.city);
		}
		if (!this.address.zip) {
			this.address.zip = $.trim(meeting.zip);
		}
		if (!this.location) {
			this.location = $.trim(meeting.location);
		} 
		if (!this.address2) {
			this.address2 = $.trim(meeting.address2);
		}
		if (!this.locationNotes) {
			this.locationNotes = $.trim(meeting.locationNotes);
		} 
	};
};
